package com.qdlc.p2p.biz.service.impl;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

import com.google.common.base.Joiner;
import com.google.common.collect.Maps;
import com.google.common.primitives.Longs;
import com.google.gson.Gson;
import com.qd.p2p.adapter.Response;
import com.qd.p2p.adapter.enums.ResponseStatus;
import com.qd.p2p.sina.pay.enums.IdentityType;
import com.qdlc.p2p.biz.processor.event.RedEnvelopeCollectNoticeEvent;
import com.qdlc.p2p.biz.processor.event.TppDispatchValueEvent;
import com.qdlc.p2p.biz.service.AbstractCollectService;
import com.qdlc.p2p.biz.service.CollectPayService;
import com.qdlc.p2p.biz.service.RPCRequestService;
import com.qdlc.p2p.common.constant.Global;
import com.qdlc.p2p.common.constant.NotifyUrl;
import com.qdlc.p2p.common.constant.RedEnvelopeType;
import com.qdlc.p2p.common.constant.RedStatus;
import com.qdlc.p2p.common.constant.SinaConstant;
import com.qdlc.p2p.common.constant.Summary;
import com.qdlc.p2p.common.constant.TradeCode;
import com.qdlc.p2p.common.constant.TradeStatus;
import com.qdlc.p2p.common.core.ReturnResult;
import com.qdlc.p2p.common.core.queue.QueueHandlers;
import com.qdlc.p2p.common.exception.RedEnvelopeException;
import com.qdlc.p2p.common.util.BigDecimalUtil;
import com.qdlc.p2p.common.util.DataUtils;
import com.qdlc.p2p.common.util.DateUtil;
import com.qdlc.p2p.common.util.MessageUtil;
import com.qdlc.p2p.common.util.StringUtil;
import com.qdlc.p2p.dal.common.SystemConfigHelper;
import com.qdlc.p2p.dal.common.SystemConfigHelper.Nid;
import com.qdlc.p2p.dal.dao.TppDao;
import com.qdlc.p2p.dal.dao.UserDao;
import com.qdlc.p2p.dal.dao.UserRedEnvelopeDao;
import com.qdlc.p2p.dal.dto.Tpp;
import com.qdlc.p2p.dal.dto.TppTradeLog;
import com.qdlc.p2p.dal.dto.User;
import com.qdlc.p2p.dal.model.TppModel;
import com.qdlc.p2p.dal.model.TppTradeLogModel;
import com.qdlc.p2p.dal.util.UserUtils;

/**
 * 红包兑换 代收服务类
 *
 * @author wuaj
 * @version 1.1.0.0
 */
@Service("redEnvelopeCollectService")
public class RedEnvelopeCollectServiceImpl extends AbstractCollectService {

    private final static String BUSINESS_NAME = "红包兑现代收-";

    /**
     * 红包ID
     */
    private ThreadLocal<long[]> redEnvelopeIds = new ThreadLocal<long[]>();

    /**
     * 用户ID
     */
    private ThreadLocal<Long> userId = new ThreadLocal<Long>();

    /**
     * 红包类型
     */
    private ThreadLocal<Byte> redType = new ThreadLocal<Byte>();

    /**
     * 红包使用场景
     */
    private ThreadLocal<String> useType = new ThreadLocal<String>();
    //double money;

    /**
     * 交易金额
     */
    private ThreadLocal<Double> money = new ThreadLocal<Double>();

    /**
     * 自动调度标识 1:自动，0:非自动
     */
    private ThreadLocal<Integer> autoDispatch = new ThreadLocal<Integer>();

    @Resource
    private UserRedEnvelopeDao userRedEnvelopeDao;

    @Resource
    private TppDao tppDao;

    @Resource
    private UserDao userDao;

    @Resource
    @Qualifier("redEnvelopePayService")
    private CollectPayService redEnvelopePayService;

    @Resource
    @Qualifier("bfRPCRequestService")
    private RPCRequestService<Response, TppTradeLog> rpcRequestService;

    @Override
    public String getCallBackUrl() {
        return WEB_URL.concat(NotifyUrl.RED_ENVELOPE_NOTIFY_COLLECT_URL).concat(".html");
    }

    @Override
    public String getBusinessName() {
        return BUSINESS_NAME;
    }

    @Override
    public void analyzePars(Object... pars) {
        this.autoDispatch.set(0);// 默认非自动
        if (pars.length != 4 && pars.length != 5) {
            throw new IllegalArgumentException("参数个数正确!");
        }
        try {
            this.redEnvelopeIds.set((long[]) pars[0]); // 红包兑现ID
            this.userId.set((Long) pars[1]);
            this.redType.set((Byte) pars[2]);
            this.useType.set((String) pars[3]);
            if (pars.length == 5) {
                this.autoDispatch.set((Integer) pars[4]);
            }
            double moneyTemp = userRedEnvelopeDao.getTotalAmountByIds(redEnvelopeIds.get(), userId.get());
            moneyTemp = BigDecimalUtil.round(moneyTemp, 2);
            money.set(moneyTemp);
        } catch (Exception ex) {
            throw new IllegalArgumentException("参数类型正确!");
        }
    }

    @Override
    public TppTradeLog getTppTradeLog() {
        // 新浪备注
        Summary summary = redType.get() == RedEnvelopeType.CASH ? Summary.CASH_RED_ENVELOPE_EXCHAGE : Summary.VIRTUAL_RED_ENVELOPE_EXCHAGE;

        Map<String, Object> memoMap = Maps.newHashMap();
        memoMap.put("redType", redType.get());// 红包类型
        memoMap.put("redIds", redEnvelopeIds.get());// 兑现红包IDS
        memoMap.put("userId", userId.get());// 兑现用户ID
        memoMap.put("useType", useType.get());// 红包用途
        memoMap.put("money", money.get());// 红包用途
        Long tenderId = DataUtils.toLong(Global.getTransfer().get("tenderId"));
        if (tenderId != null) {
            memoMap.put("keyId", tenderId);// 投标ID
        }
        String toTppUserCustId = UserUtils.getUserTppCustId(userId.get());

        TppTradeLog tppTradeLog = new TppTradeLog();
        tppTradeLog.setTradeType(TradeCode.C1003.toString());
        tppTradeLog.setTppUserCustId(SystemConfigHelper.getSystemConfig(Nid.MERCHANT_ID).getValue()); // 平台支付
        tppTradeLog.setIdentityType(IdentityType.EMAIL.toString());
        tppTradeLog.setToTppUserCustId(toTppUserCustId);
        tppTradeLog.setMemo((new Gson()).toJson(memoMap));
        tppTradeLog.setMoney(money.get());
        tppTradeLog.setServiceType(summary.getSummaryType());
        tppTradeLog.setAutoDispatch(autoDispatch.get());// 自动调度标识 1:自动，0:非自动
        return tppTradeLog;
    }

    @Override
    public ReturnResult requestOrder(TppTradeLog tppTradeLog) {
        Response response = rpcRequestService.redExchangeRequest(tppTradeLog);
        // 未知状态通知 交由消息队列处理
        if (response == null || response.getStatus() == ResponseStatus.UN_KNOW) {
            notice(tppTradeLog, TradeStatus.TRADE_STATUS_UN_KNOW);
            return new ReturnResult(true, "处理中!请查看兑现结果!");
        }
        if (response.getStatus() == ResponseStatus.SUCCESS) {
            tppTradeLog.setRemark(getBusinessName().concat("处理成功"));
            tppTradeService.update(tppTradeLog);

            // 成功通知
            notice(tppTradeLog, TradeStatus.TRADE_STATUS_FINISHED);
            requestOrderSuccess(tppTradeLog);
        } else if (response.getStatus() == ResponseStatus.FAILURE) {
            String message = response == null ? "RPC调用失败" : MessageFormat.format("{0}转账失败!ResCode:{1} ResMessage:{2}",
                    getBusinessName(), response.getResponseCode(), response.getResponseMsg());
            String responseMessage = response == null ? "RPC调用失败" : response.getResponseMsg();
            logger.info(message);

            // 置为废单
            tppTradeLog.setTradeStatus(TradeStatus.TRADE_STATUS_INVALID.getStatus());
            tppTradeLog.setRemark(getBusinessName().concat("提交失败-" + responseMessage));
            tppTradeService.update(tppTradeLog);

            requestOrderFail(tppTradeLog); // 请求失败处理
            return new ReturnResult(true, "兑现失败 - 系统繁忙请重试!");
        }
        return ReturnResult.SUCCESS;
    }

    @Override
    public boolean preCreateOrder() {
        // 红包类型
        String redTypeStr = redType.get() == RedEnvelopeType.CASH ? "现金红包" : "虚拟红包";
        // 检测红包是否可用
        boolean isCashEnvelope = userRedEnvelopeDao.checkCanUseRedEnvelope(redEnvelopeIds.get(), userId.get(), redType.get());
        if (isCashEnvelope == false) {
            String message = MessageFormat.format("无法兑现,请确认红包是否过期或非{0}!", redTypeStr);
            logger.info(message);
            throw new RedEnvelopeException(message, RedEnvelopeException.TYPE_JSON);
        }
        // 红包限制最低兑换金额,0代表不限制
        double limit = SystemConfigHelper.getDoubleValue(SystemConfigHelper.Nid.RED_ENVELOPE_EXCHANGE_LIMIT);
        // 统计可使用红包的总额,取小数点2位
        double redEnvelopeAmount = userRedEnvelopeDao.getTotalAmountByIds(redEnvelopeIds.get(), userId.get());
        redEnvelopeAmount = BigDecimalUtil.round(redEnvelopeAmount, 2);
        // 如果兑换金额小于限制金额则不允许兑换
        if (redEnvelopeAmount < limit) {
            throw new RedEnvelopeException(MessageUtil.getMessage("MF100002002"), RedEnvelopeException.TYPE_JSON);
        }
        return true;
    }

    @Override
    public void aftCreateOrder(TppTradeLog tppTradeLog) {
        //  红包冻结
        int userCount = userRedEnvelopeDao.changeStatus(redEnvelopeIds.get(), RedStatus.NO_USE, RedStatus.FREEZE);
        if (userCount != redEnvelopeIds.get().length) {
            throw new RedEnvelopeException("请确认红包状态是否可用!", RedEnvelopeException.TYPE_JSON);
        }
    }

    @Override
    public void requestOrderFail(TppTradeLog tppTradeLog) {
        // 解冻红包
        logger.error(tppTradeLog.getRemark().concat("红包解冻"));
        Map<String, Object> memoMap = DataUtils.getMemo(tppTradeLog.getMemo());
        long[] redEnvelopeIds = Longs.toArray((ArrayList) memoMap.get("redIds"));
        List<Long> ids = StringUtil.getRedIdList(redEnvelopeIds);
        byte redType = DataUtils.toByte(memoMap.get("redType")); // 红包类型
        long userId = DataUtils.toLong(memoMap.get("userId"));

        // 红包解冻
        userRedEnvelopeDao.changeStatus(redEnvelopeIds, RedStatus.FREEZE, RedStatus.NO_USE);

        // 保存调度任务信息
        // 排除手动兑现（手动兑现失败后可以重新兑现）
        if (redType == RedEnvelopeType.VIRTUAL) {
            Tpp tpp = new Tpp();
            tpp.setStatus(TppModel.STATUS_FAIL);
            tpp.setRespDesc(tppTradeLog.getRemark());
            User toUser = userDao.getUserById(userId);
            tpp.setServiceType(TppModel.RED_ENVELOPE_EXCHANGE);
            tpp.setTppType(SinaConstant.TRADE_SERVICE_RED_ENVELOPE_EXCHAGE);
            tpp.setTppUserCustId(tppTradeLog.getTppUserCustId());
            tpp.setToUser(toUser);
            tpp.setToTppUserCustId(toUser.getTppUserCustId());
            tpp.setMoney(tppTradeLog.getMoney());
            tpp.setOrderId(tppTradeLog.getOrderNo());
            tpp.setOrderDate(DateUtil.dateStr7(tppTradeLog.getAddTime()));
            tpp.setExtended(Joiner.on(",").skipNulls().join(ids));
            tpp.setTradeType(tppTradeLog.getTradeType());
            tpp.setFlag(redType + "");
            tpp.setAddTime(DateUtil.getNow());
            tppDao.save(tpp);

            // 调度任务交由队列处理(延时处理)
            if (tppTradeLog.getAutoDispatch() != TppTradeLogModel.IS_AUTO) {// 防止重复处理
                TppModel tppModel = TppModel.instance(tpp);
                TppDispatchValueEvent tppDispatchValueEvent = new TppDispatchValueEvent();
                tppDispatchValueEvent.setTppModel(tppModel);
                QueueHandlers.put(tppDispatchValueEvent);
            }
        }
    }

    @Override
    public void tradeFinish(TppTradeLog tppTradeLog, String depositStatus, double tradeAmount) {
        super.tradeFinishForStatus(tppTradeLog, depositStatus, tradeAmount);
    }

    @Override
    public void tradeSuccess(TppTradeLog tppTradeLog, String depositStatus) {
        super.tradeSuccessForStatus(tppTradeLog, depositStatus);
    }

    @Override
    public void tradeFail(TppTradeLog tppTradeLog, String depositStatus) {
        super.tradeFailForStatus(tppTradeLog, depositStatus);
        // 回调失败
        requestOrderFail(tppTradeLog);
    }

    /**
     * 发送投标通知 交由队列来执行
     */
    private void notice(TppTradeLog tppTradeLog, TradeStatus tradeStatus) {
        RedEnvelopeCollectNoticeEvent event = new RedEnvelopeCollectNoticeEvent();
        event.setClientIp(Global.getIP());
        event.setTradeStatus(tradeStatus);
        event.setTppTradeLog(tppTradeLog);
        event.setOutOrderNo(tppTradeLog.getOrderNo());
        event.setDepositAmount(tppTradeLog.getMoney());
        QueueHandlers.put(event);
    }
}